package main

import (
	"fmt"
	"log"
	"reflect"
)

// 1（10分）
// 题目：（本题目是一个动手实践类题目，需要具备C语言和数据结构基础。）
// 在课程中，我们讨论一个小型的从表达式语言Sum到栈计算机Stack的编译器，
// 在附件中，你能找到对该编译器的一个C语言实现，但这个实现是不完整的，请你
// 把缺少的代码补充完整（不超过10行代码）。
// 加分题：实现常量折叠优化。

func init() {
	log.SetFlags(log.Lshortfile)
}

func TODO() {
	log.Println("Todo")
}

type Exper interface {
	Print() string
}

type ExpInt struct {
	i int
}

func (e ExpInt) Print() string {
	return fmt.Sprintf("%d", e.i)
}

func NewExpInt(i int) Exper {
	return &ExpInt{i}
}

type ExpSum struct {
	left  Exper
	right Exper
}

func NewExpSum(left, right Exper) Exper {
	return &ExpSum{left, right}
}

func (e ExpSum) Print() string {
	return e.left.Print() + "+" + e.right.Print()
}

type Stacker interface {
	Print() string
}

type Stack struct {
	top  *Element
	size int
}

type Element struct {
	value Stacker
	next  *Element
}

func (s *Stack) Len() int {
	return s.size
}

func (s *Stack) Push(value Stacker) {
	s.top = &Element{value, s.top}
	s.size++
}

func (s *Stack) Pop() (value Stacker) {
	if s.size > 0 {
		value, s.top = s.top.value, s.top.next
		s.size--
		return
	}
	panic("statck empty")
}
func (s *Stack) Print() {
	fmt.Println("---stack start---")
	var t *Element
	t = s.top
	for i := 0; i < s.size; i++ {
		// fmt.Println(...) reflect.TypeOf(t.value)
		fmt.Println(reflect.TypeOf(t.value), t.value)
		t = t.next
	}
	fmt.Println("---stack end---\n")
}

type StackAdd struct {
}

func (e StackAdd) Print() string {
	return "StackAdd"
}

type StackPush struct {
	i int
}

func (e StackPush) Print() string {
	return fmt.Sprintf("StackPush %d", e.i)
}

func compile(e Exper) (all Stack) {
	var f func(e Exper)
	f = func(e Exper) {
		switch t := e.(type) {
		case *ExpInt:
			all.Push(&StackPush{t.i})
		case *ExpSum:
			f(t.left)
			f(t.right)
			all.Push(&StackAdd{})
		}
	}
	f(e)
	return
}

func main() {
	log.Println("Compile starting\n")
	e := NewExpSum(
		NewExpSum(
			NewExpInt(2),
			NewExpInt(3),
		),
		NewExpInt(4),
	)
	// pp.Println(e)
	log.Println("Exp_Print-----------")
	log.Println(e.Print())
	log.Println("Compile-------------")
	s := compile(e)
	// pp.Println(s)
	s.Print()
}
